package org.poptest.junit4;


import org.poptest.junit4.annotation.Notify;
import org.junit.runner.notification.RunListener;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.model.InitializationError;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Test Runner that adds Listeners to Junit.
 * Listeners are declared in Notify annotation
 * Eg.
 * @RunWith(NotifyingRunner.class)
 * @Notify(listener=LoggingListener.class)
 *
 * @see org.poptest.junit4.annotation.Notify
 * @see DelegatingRunner
 *
 * @author Alan Roche
 *         Date: 23/01/11
 *         Time: 22:32
 */
public class NotifyingRunner extends DelegatingRunner {
    private final List <RunListener> listeners;
    public NotifyingRunner(final Class<?> testClass) throws InitializationError {
        super(testClass, Notify.class, "delegateTo");
        listeners = resolveListeners(testClass);
    }

    public NotifyingRunner(final Class<?> testClass,
                           final Class<? extends Annotation> delegateAnnotation,
                           final String delegateTypeAccessor) throws InitializationError {
        super(testClass, delegateAnnotation, delegateTypeAccessor);
        listeners = resolveListeners(testClass);
    }

    private List<RunListener> resolveListeners(final Class<?> testClass) {
        // Get the listener types from the notifier annotation
        final List<Class<? extends RunListener>> listenerTypes = new ArrayList<Class<? extends RunListener>>();
        final List <RunListener> listeners = new ArrayList<RunListener>(listenerTypes.size());
        try {
            final Notify notifier = testClass.getAnnotation(Notify.class);
            if (notifier != null) {
                if (notifier.listeners() != null && notifier.listeners().length > 0) {
                    listenerTypes.addAll(Arrays.asList(notifier.listeners()));
                }

                // Instantiate the listeners
                for (final Class<? extends RunListener> listenerType : listenerTypes) {
                    listeners.add(listenerType.newInstance());
                }
            }
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        return listeners;
    }

    @Override
    public void run(final RunNotifier runNotifier) {
        for (final RunListener listener : listeners) {
            runNotifier.addListener(listener);
        }
        super.run(runNotifier);
    }
}
